home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / video / fly8111-.000 / fly8111- / fly8 / memory.c < prev    next >
C/C++ Source or Header  |  1979-12-31  |  16KB  |  775 lines

  1. /* --------------------------------- memory.c ------------------------------- */
  2.  
  3. /* This is part of the flight simulator 'fly8'.
  4.  * Author: Eyal Lebedinsky (eyal@ise.canberra.edu.au).
  5. */
  6.  
  7. /* A general purpose memory manager.
  8.  *
  9.  * This one keeps a small header in front of all blocks (both free and
  10.  * allocated) to enable merging adjacent ones. A separate quick access
  11.  * by size list is also maintained for the allocation part. It does not
  12.  * use the size in the free() call since it has it's own.
  13.  *
  14.  * Memory usage is a 512 entries table of BLOCK (one pointer each) and two
  15.  * shorts for each memory block (suppose we have a 1000 blocks, then we use
  16.  * 4kb for block headers and 2kb for the fixed table).
  17. */
  18.  
  19. #include "fly.h"
  20.  
  21.  
  22. #define GRAIN        8
  23.  
  24. #define NBLOCKS        512
  25.  
  26. #define MAXBLOCK    (NBLOCKS*GRAIN)
  27.  
  28. #define BLOCKHEADER    (2 * sizeof (SIZE (NULL)))
  29.  
  30. #define MAXBYTES    (MAXBLOCK - BLOCKHEADER)
  31.  
  32. #define MINBYTES    (BLOCKHEADER + 2 * sizeof (NEXT (NULL)))
  33.  
  34. #define BYTESINDEX(n) \
  35.     ((Uint)(((n) < MINBYTES ? MINBYTES : (n)) - 1) / GRAIN)
  36.  
  37. #define INDEXSIZE(i)    (((i) + 1) * GRAIN)
  38.  
  39. #define CHUNKHEADER    (offsetof (CHUNK, buff) + GRAIN)
  40.  
  41. #define CHUNKSIZE    (MAXBLOCK*1)
  42.  
  43. #define CHUNKMIN    (CHUNKSIZE/4)
  44.  
  45. #define CHUNKPART    (CHUNKMIN/2)
  46.  
  47. #define MEM_MINLOG    0            /* log nothing */
  48.  
  49. #define SIZE(p)        ((short *)(p))[-1]
  50. #define PSIZE(p)    ((short *)(p))[-2]
  51. #define NEXT(p)        ((char **)(p))[0]
  52. #define PREV(p)        ((char **)(p))[1]
  53.  
  54. #define DLLADD(n,b) \
  55.     do { \
  56.         char    *z; \
  57.         if (T(z = NEXT (b) = blocks[n].chain)) { \
  58.             PREV (b) = PREV (z); \
  59.             PREV (z) = b; \
  60.         } else \
  61.             PREV (b) = (char *)&blocks[n].chain; \
  62.         blocks[n].chain = b; \
  63.     } while (0)
  64.  
  65. #define DLLREMOVE(b) \
  66.     do { \
  67.         char    *z; \
  68.         if (T(z = NEXT (b))) \
  69.             PREV (z) = PREV (b); \
  70.         NEXT (PREV (b)) = z; \
  71.     } while (0)
  72.  
  73. #define DLLTRIMHEAD(n,b) \
  74.     do { \
  75.         char    *z; \
  76.         if (T(z = blocks[n].chain = NEXT (b))) \
  77.             PREV (z) = PREV (b); \
  78.     } while (0)
  79.  
  80. /* Memory is acquired in large chunks to later be carved into allocated
  81.  * blocks.
  82.  *
  83.  * Each chunck is stored as a list of blocks. The first one is pointed at
  84.  * by chunk->chain, then each block follows on block->next. The last block
  85.  * on a chunk list is always a dummy block with a size of zero (it only has
  86.  * a header, no body).
  87. */
  88. typedef struct chunk    CHUNK;
  89. struct chunk {
  90.     CHUNK    *next;
  91.     Ushort    size;
  92.     long    buff[1];            /* need to force alignment */
  93. };
  94.  
  95. /* A block has a header with the block (net) size and the size of the previous
  96.  * block (as the dll ptr). Free blocks are linked to another list (based on
  97.  * the block size) using block->next.
  98. */
  99.  
  100. /* Free blocks are kept on a list by size. It is just a simple pointer which
  101.  * may have some stats attached.
  102. */
  103. typedef struct blocks    BLOCKS;
  104. struct blocks {
  105.     char    *chain;        /* note: sometimes used as NEXT(block)!!! */
  106. #ifdef MEM_STATS
  107.     short    nused;                /* stats: good alloc - free */
  108.     Ulong    nalloc;                /* stats: requested alloc */
  109.     Ulong    nomem;                /* stats: memory short count */
  110. #endif
  111. };
  112.  
  113. static BLOCKS    *blocks = 0;
  114. static CHUNK    *chunks = 0;            /* used very little */
  115. static Uint    malloc_dead = 0;        /* malloc failed! */
  116.  
  117. static int    debugging = 0;            /* internal, leave alone */
  118. static int    logging = 0;            /* set to 1 for logging */
  119.  
  120. /* Some debugging functions first
  121. */
  122. LOCAL_FUNC void * NEAR
  123. verify_address (char *p, char *title)
  124. {
  125.     CHUNK    *c;
  126.  
  127.     if ( SIZE (p) <= 0 ||  SIZE (p) > MAXBLOCK ||
  128.         PSIZE (p) <  0 || PSIZE (p) > MAXBLOCK) {
  129.         LogPrintf ("verify_address(%s)> bad %d/%p P %d\n",
  130.             title, SIZE (p), p, PSIZE (p));
  131.         return (NULL);
  132.     }
  133.     for (c = chunks; c; c = c->next) {
  134.         if (p            >= GRAIN           + (char *)c->buff &&
  135.             SIZE (p) + p <= GRAIN + c->size + (char *)c->buff)
  136.             return (p);
  137.     }
  138.     LogPrintf ("verify_address(%s)> stray %d/%p P %d\n",
  139.         title, SIZE (p), p, PSIZE (p));
  140.     return (NULL);
  141. }
  142.  
  143. /* It should be noted that it is resonable to see spurious assert failures.
  144.  * These are the result of an a syncronous memory mgmt call done during the
  145.  * check. The only way to avoid it is to lock for the full check, which is
  146.  * too risky.
  147.  *
  148.  * Basically, if a problem shows and then goes away then it is OK. If it
  149.  * stays then it is real.
  150. */
  151. LOCAL_FUNC void NEAR
  152. mem_assert (char *title)
  153. {
  154.     int    i;
  155.     int    n;
  156.     int    t;
  157.     char    *p;
  158.     long    freebytes = 0;
  159.     CHUNK    *c;
  160.     Ulong    flags;
  161.  
  162.     if (!blocks)
  163.         return;
  164.  
  165. /* check the free blocks list
  166. */
  167.     freebytes = STATS_MEMALLOCED;
  168.     for (i = 0; i < NBLOCKS; ++i) {
  169.         n = INDEXSIZE (i);
  170.         flags = Sys->Disable ();
  171.         for (p = blocks[i].chain; p; p = NEXT (p)) {
  172.             if (SIZE (p) !=  (short)n) {
  173. LogPrintf ("assert(%s): %5d - Bad %d/%p P %d\n",
  174.                     title, n, SIZE (p), p, PSIZE (p));
  175.                 break;
  176.             }
  177.             freebytes += n;
  178.         }
  179.         Sys->Enable (flags);
  180.     }
  181.     if (T(freebytes -= STATS_MEMTOTAL))
  182.         LogPrintf ("assert(%s): leakage %9ld\n", title, -freebytes);
  183.  
  184. /* check the chunks structure (free and allocated).
  185. */
  186.     i = 0;
  187.     for (c = chunks; c; c = c->next) {
  188.         ++i;
  189.         t = 0;
  190.         flags = Sys->Disable ();
  191.         for (n = 0, p = GRAIN + (char *)c->buff;;) {
  192.             if (PSIZE (p) != n) {
  193. LogPrintf ("assert(%s): %d[%ld]: bad P %d (!= %d)\n",
  194.                     title, i, t, PSIZE (p), n);
  195.                 break;
  196.             }
  197.             if (0 == (n = SIZE (p)))
  198.                 break;
  199.             if (n < 0)
  200.                 n = -n;
  201.             p += n;
  202.             t += n;
  203.         }
  204.         Sys->Enable (flags);
  205.         if ((Ushort)t != c->size) {
  206.             LogPrintf ("assert(%s): %d: bad length %ld\n",
  207.                 title, i, t);
  208.         }
  209.     }
  210. }
  211.  
  212. /* A set of safe memory management functions.
  213. */
  214.  
  215. extern void * FAR
  216. xmalloc (Uint size)
  217. {
  218.     Ulong    flags;
  219.     void    *p;
  220.  
  221.     if (!malloc_dead) {
  222.         flags = Sys->Disable ();
  223.         if (F(p = malloc (size))) {
  224.             malloc_dead = 1;
  225.             ++STATS_MEMLOW;
  226.             ++STATS_MEMNO;
  227.         }
  228.         Sys->Enable (flags);
  229.     } else {
  230.         ++STATS_MEMLOW;
  231.         ++STATS_MEMNO;
  232.         p = NULL;
  233.     }
  234.     return (p);
  235. }
  236.  
  237. extern void * FAR
  238. xcalloc (Uint count, Uint size)
  239. {
  240.     Ulong    flags;
  241.     void    *p;
  242.  
  243.     if (!malloc_dead) {
  244.         flags = Sys->Disable ();
  245.         if (F(p = calloc (count, size))) {
  246.             malloc_dead = 1;
  247.             ++STATS_MEMLOW;
  248.             ++STATS_MEMNO;
  249.         }
  250.         Sys->Enable (flags);
  251.     } else {
  252.         ++STATS_MEMLOW;
  253.         ++STATS_MEMNO;
  254.         p = NULL;
  255.     }
  256.     return (p);
  257. }
  258.  
  259. extern char * FAR
  260. xstrdup (const char *s)
  261. {
  262.     char    *p;
  263.     Ulong    flags;
  264.  
  265.     flags = Sys->Disable ();
  266.     if (F(p = strdup (s))) {
  267.         malloc_dead = 1;
  268.         ++STATS_MEMLOW;
  269.         ++STATS_MEMNO;
  270.     }
  271.     Sys->Enable (flags);
  272.     return (p);
  273. }
  274.  
  275. extern void * FAR
  276. xfree (void *block)
  277. {
  278.     Ulong    flags;
  279.  
  280.     if (!block)
  281.         return (0);
  282.  
  283.     flags = Sys->Disable ();
  284.     free (block);
  285.     Sys->Enable (flags);
  286.     malloc_dead = 0;
  287.     return (0);
  288. }
  289.  
  290. /* We get here if the free blocks list cannot satisfy a memory allocation
  291.  * request.
  292. */
  293. LOCAL_FUNC int NEAR
  294. mem_topup (void)
  295. {
  296.     CHUNK    *c;
  297.     char    *b;
  298.     int    n;
  299.  
  300.     if (malloc_dead)
  301.         return (malloc_dead);
  302.  
  303.     for (n = CHUNKSIZE; n > CHUNKMIN; n -= CHUNKPART) {
  304.         if (T(c = (CHUNK *)malloc (CHUNKHEADER + GRAIN + n)))
  305.             break;
  306.     }
  307.  
  308.     if (F(c)) {
  309.         if (logging && !(st.flags1 & SF_ASYNC))
  310.             LogPrintf ("malloc() dead\n");
  311.         malloc_dead = 1;
  312.         ++STATS_MEMLOW;
  313.     } else {
  314.  
  315. /* add new chunk to chunks list.
  316. */
  317.         c->next = chunks;
  318.         chunks = c;
  319.         c->size = (Ushort)n;
  320.         STATS_MEMTOTAL += n;
  321.  
  322. /* build EOL block
  323. */
  324.         b = GRAIN + n + (char *)c->buff;
  325.         SIZE (b) = 0;
  326.         PSIZE (b) = (short)n;
  327.  
  328.         if (logging && !(st.flags1 & SF_ASYNC))
  329.             LogPrintf ("chunk EOL  is %d/%p\n", SIZE (b), b);
  330.  
  331. /* build root block.
  332. */
  333.         b = GRAIN + (char *)c->buff;
  334.         SIZE (b) = (short)n;
  335.         PSIZE (b) = 0;
  336.  
  337.         if (logging && !(st.flags1 & SF_ASYNC))
  338.             LogPrintf ("chunk root is %d/%p\n", SIZE (b), b);
  339.  
  340. /* add root to list by size.
  341. */
  342.         n = BYTESINDEX (n);
  343.         DLLADD (n, b);
  344.     }
  345.     return (malloc_dead);
  346. }
  347.  
  348. /* Try to satisfy a memory request by splitting a larger size block from
  349.  * the free memory list.
  350. */
  351. LOCAL_FUNC char * NEAR
  352. mem_reuse (Uint n)
  353. {
  354.     Uint    i;        /* running block size index */
  355.     Uint    j;        /* first block size to split */
  356.     Uint    bytes;
  357.     char    *t;        /* block allocated */
  358.     char    *p;        /* block leftover */
  359.     char    *q;        /* following block */
  360.  
  361. /* j is the largest size that is too small to split. If available then it will
  362.  * be returned as is.
  363. */
  364.     j = n + 1 + BYTESINDEX (MINBYTES);
  365.     if (j > NBLOCKS)
  366.         j = NBLOCKS;
  367.  
  368.     for (i = n; i < j; ++i) {
  369.         if (F(p = blocks[i].chain))
  370.             continue;
  371.  
  372.         DLLTRIMHEAD (i, p);
  373.         return (p);
  374.     }
  375.  
  376.     p = NULL;
  377.     for (; i < NBLOCKS; ++i) {
  378.         if (F(p = blocks[i].chain))
  379.             continue;
  380.  
  381.         if (logging && debugging && !(st.flags1 & SF_ASYNC))
  382.             LogPrintf ("split [%d] %d/%p", i, SIZE (p), p);
  383.  
  384. /* remove p from list by size.
  385. */
  386.         DLLTRIMHEAD (i, p);
  387.  
  388. /* we modify three blocks now, establish addressability.
  389. */
  390.         bytes = INDEXSIZE (n);            /* bytes allocated */
  391.         t = p;                    /* retained block */
  392.         q = t + SIZE (t);            /* next block */
  393.         p = q - bytes;                /* returned block */
  394.  
  395. /* update headers.
  396. */
  397.         PSIZE (q) = SIZE (p) = (short)bytes;
  398.         PSIZE (p) = (SIZE (t) -= (short)bytes);
  399.  
  400. /* add (leftover) t to list by size;
  401. */
  402.         i -= n + 1;
  403.         DLLADD (i, t);
  404.  
  405.         if (logging && debugging && !(st.flags1 & SF_ASYNC))
  406.             LogPrintf (" -> [%d] %d/%p + %d/%p\n",
  407.             i, SIZE (t), t, SIZE (p), p);
  408.         break;
  409.     }
  410.  
  411.     return (p);
  412. }
  413.  
  414. extern void * FAR
  415. mem_alloc (Uint bytes)
  416. {
  417.     char    *p;
  418.     Uint    i;
  419.     Ulong    flags;
  420.  
  421.     if (!bytes || !blocks) {
  422.         p = NULL;
  423.         goto ret;
  424.     }
  425.  
  426.     if (bytes > MAXBYTES) {
  427.         p = xmalloc (bytes);
  428.         goto ret;
  429.     }
  430.  
  431.     i = bytes + BLOCKHEADER;
  432.     i = BYTESINDEX (i);
  433.     flags = Sys->Disable ();
  434.  
  435. /* We get memory by first checking for a reusable block, then trying to
  436.  * acquire fresh memory.
  437.  * The loop should terminate by either getting a block or by running out of
  438.  * system memory.
  439. */
  440.     while (F(p = mem_reuse (i)) && !mem_topup ())
  441.         ;
  442.  
  443. #ifdef MEM_STATS
  444.     ++blocks[i].nalloc;
  445.     if (p)
  446.         ++blocks[i].nused;
  447.     else
  448.         ++blocks[i].nomem;
  449. #endif
  450.     if (p) {
  451.         STATS_MEMALLOCED += SIZE (p);
  452.         if (STATS_MEMALLOCED > STATS_MEMMAXUSED)
  453.             STATS_MEMMAXUSED = STATS_MEMALLOCED;
  454.         SIZE (p) = -SIZE (p);            /* is now alloc'ed */
  455.     } else
  456.         ++STATS_MEMNO;
  457.  
  458.     Sys->Enable (flags);
  459. ret:
  460.     if (p)
  461.         memset (p, 0, bytes);
  462.     return (p);
  463. }
  464.  
  465. extern void * FAR
  466. memd_alloc (Uint bytes, char *file, int lineno)
  467. {
  468.     void    *p;
  469.  
  470.     debugging = 1;
  471.     p = mem_alloc (bytes);
  472.     debugging = 0;
  473.  
  474.     if (logging && !(st.flags1 & SF_ASYNC) && bytes >= MEM_MINLOG)
  475.         LogPrintf ("alloc %s(%d) %u/%p\n", file, lineno, bytes, p);
  476.  
  477.     return (p);
  478. }
  479.  
  480. /* A fast memory manager. A freed block is merged with neighbouring blocks
  481.  * rather than just get added to the pool.
  482. */
  483. extern void * FAR
  484. mem_free (void *block, int bytes)
  485. {
  486.     char    *p;
  487.     char    *t;
  488.     Ulong    flags;
  489.     int    n;
  490.  
  491.     if (!block || bytes < 0)
  492.         return (NULL);
  493.  
  494.     if (!bytes || bytes > MAXBYTES || !blocks)
  495.         return (xfree (block));
  496.  
  497.     flags = Sys->Disable ();
  498.  
  499.     p = block;
  500.     block = NEXT (block);            /* for the return(p) */
  501.  
  502. #ifdef CHECK_MEM
  503. if (-SIZE (p) < INDEXSIZE (BYTESINDEX (BLOCKHEADER + bytes))) {
  504.     LogPrintf ("mem_free> %d bad block %d/%p\n", bytes, SIZE (p), p);
  505.     Sys->Enable (flags);
  506.     return (NULL);
  507. }
  508. #endif
  509.  
  510.     SIZE (p) = -SIZE (p);            /* is now free */
  511.  
  512.     bytes = SIZE (p);
  513.  
  514.     STATS_MEMALLOCED -= bytes;
  515.  
  516. #ifdef MEM_STATS
  517.     n = BYTESINDEX (bytes);
  518.     --blocks[n].nused;
  519.     if (logging && !(st.flags1 & SF_ASYNC) && blocks[n].nused < 0)
  520.         LogPrintf ("bad free count (%d)\n", bytes);
  521. #else
  522.     if (logging && !(st.flags1 & SF_ASYNC) && STATS_MEMALLOCED < 0)
  523.         LogPrintf ("bad free count (%d)\n", bytes);
  524. #endif
  525.  
  526. /* merge p into preceding blocks.
  527. */
  528.     while (PSIZE (p) && (t = p - PSIZE (p), SIZE (t) > 0)) {
  529.         DLLREMOVE (t);
  530.         SIZE (t) += SIZE (p);            /* merge p into t */
  531.         p = t;
  532.     }
  533.  
  534. /* merge following blocks into p.
  535. */
  536.     while (t = p + SIZE (p), SIZE (t) > 0) {
  537.         DLLREMOVE (t);
  538.         SIZE (p) += SIZE (t);            /* merge t into p */
  539.     }
  540.     PSIZE (t) = SIZE (p);
  541.  
  542. /* add p to list by size.
  543. */
  544.     n = BYTESINDEX (SIZE (p));
  545.     DLLADD (n, p);
  546.  
  547.     Sys->Enable (flags);
  548.  
  549.     return (block);
  550. }
  551.  
  552. extern void * FAR
  553. memd_free (void *block, int bytes, char *file, int lineno)
  554. {
  555.     if (logging && !(st.flags1 & SF_ASYNC) && bytes >= MEM_MINLOG)
  556.         LogPrintf ("free %s(%d) %u/%p\n",
  557.             file, lineno, bytes, block);
  558.  
  559.     return (mem_free (block, bytes));
  560. }
  561.  
  562. extern char * FAR
  563. mem_strdup (const char *s)
  564. {
  565.     char    *p;
  566.     int    len;
  567.  
  568.     if (s) {
  569.         if (T(p = mem_alloc (len = strlen (s) + 1)))
  570.             memcpy (p, s, len);
  571.     } else
  572.         p = NULL;
  573.     return (p);
  574. }
  575.  
  576. extern char * FAR
  577. memd_strdup (const char *s, char *file, int lineno)
  578. {
  579.     char    *p;
  580.     int    len;
  581.  
  582.     if (logging && !(st.flags1 & SF_ASYNC)
  583.                     && (!s || strlen(s)+1 >= MEM_MINLOG))
  584.         LogPrintf ("strdup %s(%d) \"%s\"\n", file, lineno,
  585.             s ? s : "(null)");
  586.  
  587.     if (s) {
  588.         if (T(p = memd_alloc (len = strlen (s) + 1, file, lineno)))
  589.             memcpy (p, s, len);
  590.     } else
  591.         p = NULL;
  592.     return (p);
  593. }
  594.  
  595. extern void * FAR
  596. mem_strfree (char *s)
  597. {
  598.     if (s)
  599.         memory_free (s, strlen (s) + 1);
  600.     return (NULL);
  601. }
  602.  
  603. extern void * FAR
  604. memd_strfree (char *s, char *file, int lineno)
  605. {
  606.     if (logging && !(st.flags1 & SF_ASYNC))
  607.         LogPrintf ("strfree %s(%d) \"%s\"\n", file, lineno,
  608.             s ? s : "(null)");
  609.  
  610.     if (s)
  611.         memd_free (s, strlen (s) + 1, file, lineno);
  612.     return (NULL);
  613. }
  614.  
  615. /* Ensure we are not low on memory. Not yet used.
  616. */
  617. extern void FAR
  618. mem_check (void)
  619. {
  620.     Ulong    flags;
  621.  
  622. #ifdef CHECK_MEM
  623.     mem_assert ("mem_check");
  624. #endif
  625.     if (malloc_dead)
  626.         return;
  627.  
  628.     if (STATS_MEMTOTAL - STATS_MEMMAXUSED < CHUNKSIZE) {
  629.         flags = Sys->Disable ();
  630.         mem_topup ();
  631.         Sys->Enable (flags);
  632.     }
  633. }
  634.  
  635. extern int FAR
  636. mem_init (void)
  637. {
  638.     int    i;
  639.  
  640.     STATS_MEMTOTAL = 0;
  641.     STATS_MEMMAXUSED = 0;
  642.     malloc_dead = 0;
  643.     chunks = NULL;
  644.     if (F(blocks = (BLOCKS *)xcalloc (NBLOCKS, sizeof (*blocks))))
  645.         return (1);
  646.  
  647.     for (i = 0; i < NBLOCKS; ++i) {
  648.         blocks[i].chain = NULL;
  649. #ifdef MEM_STATS
  650.         blocks[i].nalloc = 0;
  651.         blocks[i].nused = 0;
  652.         blocks[i].nomem = 0;
  653. #endif
  654.     }
  655.  
  656.     return (0);
  657. }
  658.  
  659. extern void FAR
  660. mem_term (void)
  661. {
  662.     char    *p;
  663.     CHUNK    *c, *c1;
  664.     int    i, n;
  665.     long    tt;
  666.     Ulong    size;
  667.     Ulong    totn;
  668.     Ulong    freebytes;
  669. #ifdef MEM_STATS
  670.     Ulong    totalloc;
  671.     Ulong    totnomem;
  672. #endif
  673.  
  674. logging = 1;
  675.     if (!blocks)
  676.         return;
  677.  
  678.     mem_assert ("mem_term");
  679.  
  680.     totn = freebytes = 0;
  681. #ifdef MEM_STATS
  682.     totalloc = totnomem = 0;
  683. #endif
  684.     if (logging) {
  685.         LogPrintf ("Memory usage summary:\n\n");
  686.         LogPrintf ("%s%s\n", "Size  Count     Bytes",
  687. #ifdef MEM_STATS
  688.             "   nAllocs   nFailed     nUsed");
  689. #else
  690.             "");
  691. #endif
  692.     }
  693.     for (i = 0; i < NBLOCKS; ++i) {
  694.         for (n = 0, p = blocks[i].chain; p; p = NEXT (p))
  695.             ++n;
  696. #ifdef MEM_STATS
  697.         if (n || blocks[i].nalloc) {
  698.             totalloc += blocks[i].nalloc;
  699.             totnomem += blocks[i].nomem;
  700. #else
  701.         if (n) {
  702. #endif
  703.             totn += n;
  704.             tt = INDEXSIZE (i) * (long)n;
  705.             freebytes += tt;
  706.             if (logging) {
  707.                 LogPrintf ("%4u %6u %9lu",
  708.                     INDEXSIZE (i), n, tt);
  709. #ifdef MEM_STATS
  710.                 LogPrintf (" %9lu", blocks[i].nalloc);
  711.                 if (blocks[i].nomem || blocks[i].nused)
  712.                     LogPrintf (" %9lu %9d",
  713.                         blocks[i].nomem,
  714.                         blocks[i].nused);
  715. #endif
  716.                 LogPrintf ("\n");
  717.             }
  718.         }
  719.     }
  720.     if (logging) {
  721.         LogPrintf (" tot %6lu %9lu", totn, freebytes);
  722. #ifdef MEM_STATS
  723.         LogPrintf (" %9lu %9lu", totalloc, totnomem);
  724. #endif
  725.         LogPrintf ("\n%s%s\n", "Size  Count     Bytes",
  726.                 "   nAllocs   nFailed     nUsed");
  727.     }
  728.  
  729.     n = 0;
  730.     size = 0;
  731.     if (logging)
  732.         LogPrintf ("\nChunk      Size Address range\n");
  733.     for (c1 = chunks; T(c = c1);) {
  734.         ++n;
  735.         if (logging)
  736.             LogPrintf ("%5d %9u %p-%p\n", n, c->size,
  737.                 GRAIN + (char *)c->buff,
  738.                 c->size + (char *)c->buff);
  739.         c1 = c->next;
  740.         size += c->size;
  741.         xfree (c);
  742.     }
  743.     chunks = 0;
  744.     if (logging)
  745.         LogPrintf ("total %9lu\n\n", size);
  746.  
  747.     LogPrintf ("Max mem  %9lu\n", STATS_MEMMAXUSED);
  748.     LogPrintf ("Alloc'ed %9lu\n", STATS_MEMTOTAL);
  749.     LogPrintf ("Free     %9lu\n", freebytes);
  750.     LogPrintf ("Leakage  %9ld\n", STATS_MEMTOTAL - freebytes);
  751.  
  752.     blocks = xfree (blocks);
  753. }
  754.  
  755. #undef GRAIN
  756. #undef NBLOCKS
  757. #undef MAXBLOCK
  758. #undef BLOCKHEADER
  759. #undef MAXBYTES
  760. #undef MINBYTES
  761. #undef BYTESINDEX
  762. #undef INDEXSIZE
  763. #undef CHUNKHEADER
  764. #undef CHUNKSIZE
  765. #undef CHUNKMIN
  766. #undef CHUNKPART
  767. #undef MEM_MINLOG
  768. #undef SIZE
  769. #undef PSIZE
  770. #undef NEXT
  771. #undef PREV
  772. #undef DLLADD
  773. #undef DLLREMOVE
  774. #undef DLLTRIMHEAD
  775.